const TokenType = {
  LEFT_OBJECT: "{",
  RIGHT_OBJECT: "}",
  LEFT_ARRAY: "[",
  RIGHT_ARRAY: "]",
  NUMBER: "NUMBER",
  STRING: "STRING",
  BOOLEAN: "BOOLEAN",
  NULL: "null",
  COMMA: ",",
  COLON: ":",
};

// 处理的阶段
const ParseStatus = {
  ON_PARSE: 1, // 语法解析阶段
  ON_GENERATE: 2 // 生成阶段
}

class JParser {
  /**
   *@constructor
   *@param {String} jsonStr - json字符串
   *@param {Function} receiver - 可选，对每个键值对的处理函数
   */
  constructor(jsonStr, receiver) {
    this.jsonStr = jsonStr;
    // 解析阶段正在处理的token索引
    this._index = 0;
    // 生成阶段正在处理的token索引
    this._generateIndex = 0;
    this.receiver = receiver;
    this._tokenList = [];
    this.status = ParseStatus.ON_PARSE
    this.parseSyntax();
  }

  getStackTop(stack) {
    return stack.length > 0 ? stack[stack.length-1] : undefined
  }

  generate(){
    this.status = ParseStatus.ON_GENERATE;
    const result = this.generateAll()
    // 生成完毕后，索引应指向最后一个元素 避免 {a:1}}等情况
    if(this._generateIndex != this._tokenList.length - 1) {
      this.emitError()
    }
    return result
  } 
  // 根据token生成js对象
  generateAll() {
    const tokenList = this._tokenList;
    let value;
    if (this.isBasicType(tokenList[this._generateIndex])) {
      value = tokenList[this._generateIndex].value;
    }
    if (tokenList[this._generateIndex].type === TokenType.LEFT_OBJECT) {
      value = this.generateObject();
    }
    if (tokenList[this._generateIndex].type === TokenType.LEFT_ARRAY) {
      value = this.generateArray();
    }

    if(value === undefined) {
      this.emitError()
    }

    return value;
  }

  generateArray() {
    const tokenList = this._tokenList;
    let array = [];
    let operateStack = [tokenList[this._generateIndex]]
    this.next()
    let valueStack = []
    while (operateStack.length !=0 && this._generateIndex < tokenList.length) {
        const token = tokenList[this._generateIndex];
        const topToken = this.getStackTop(operateStack);
        switch (token.type) {
          case TokenType.LEFT_OBJECT:
          case TokenType.LEFT_ARRAY:
          case TokenType.STRING:
          case TokenType.NUMBER:
          case TokenType.BOOLEAN:
          case TokenType.NULL:
            if(valueStack.length === 0) {
              valueStack.push({type: '',value: this.generateAll()})
              this.next()
            }else {
              this.emitError()
            }
            break;
          case TokenType.COMMA:
            if(valueStack.length === 1) {
                let item = valueStack.pop().value
              array.push(item)
              this.next()
            }else {
              this.emitError()
            }
            break;
          case TokenType.RIGHT_ARRAY:
            if(valueStack.length === 1 && topToken.type === TokenType.LEFT_ARRAY) {
              let item = valueStack.pop().value
              array.push(item)
              operateStack = []
            }else {
              operateStack = []
            }
            break;
          default:
            this.emitError()
            break;
        }
      
    }

    if(operateStack.length !== 0 || valueStack.length !== 0) {
      this.emitError()
    }

    return array;
  }

  generateObject() {
    const tokenList = this._tokenList;
    let object = {};
    // 先将 {  入栈
    let operateStack = [tokenList[this._generateIndex]]
    this.next()
    let valueStack = []
    while (operateStack.length !=0 && this._generateIndex < tokenList.length) {

        const token = tokenList[this._generateIndex];
        const topToken = this.getStackTop(operateStack);
        switch (token.type) {
          case TokenType.LEFT_OBJECT:
          case TokenType.LEFT_ARRAY:
            if(topToken.type === TokenType.COLON && valueStack.length === 1) {
              // 递归生成对象或数组
              valueStack.push({type:'', value: this.generateAll()})
              this.next()
            }else{
              this.emitError()
            }
            break;
          case TokenType.STRING:
            // 作为键
            if(topToken.type === TokenType.LEFT_OBJECT && valueStack.length === 0) {
              valueStack.push(token)
              this.next()
            }
            // 作为值
            else if (topToken.type === TokenType.COLON && valueStack.length === 1) {
              valueStack.push(token)
              this.next()
            }else {
              this.emitError()
            }
            break;
          case TokenType.COLON:
            if(topToken.type === TokenType.LEFT_OBJECT && valueStack.length === 1) {
              operateStack.push(token);
              this.next()
            }else{
              this.emitError()
            }
            break;
          case TokenType.NULL:
          case TokenType.BOOLEAN:
          case TokenType.NUMBER:
            if (topToken.type === TokenType.COLON && valueStack.length === 1) {
              valueStack.push(token)
              this.next()
            }else {
              this.emitError()
            }
            break;
          case TokenType.COMMA:
            if(topToken.type === TokenType.COLON && valueStack.length === 2) {
              operateStack.pop()
              let value = valueStack.pop().value
              let key = valueStack.pop().value
              object[key] = this.receiver ? this.receiver(key, value) : value;
              this.next()
            }else {
              this.emitError()
            }
            break;
          case TokenType.RIGHT_OBJECT:
            if(topToken.type === TokenType.LEFT_OBJECT && valueStack.length === 0) {
              operateStack.pop()
            }else if (topToken.type === TokenType.COLON && valueStack.length === 2) {
              let value = valueStack.pop().value
              let key = valueStack.pop().value
              object[key] = this.receiver ? this.receiver(key, value) : value;
              operateStack = []
            }else {
              this.emitError()
            }  
            break;
          default:
            this.emitError()
            break;
        }
      
    }

    if(operateStack.length !== 0 || valueStack.length !== 0) {
      this.emitError()
    }

    return object;
  }

  next() {
    this._generateIndex++;
  }

  // 语法解析
  parseSyntax() {
    while (this._index < this.jsonStr.length) {
      // 跳过空格的解析
      if (this.jsonStr[this._index] === " ") {
        this._index++;
        continue;
      }
      // 记录token出现的起始位置
      let startIndex = this._index;
      const token = {...this.getToken(this.jsonStr[this._index]), index: startIndex};
      this._tokenList.push(token);
    }
  }

  emitError(errMsg) {
    if(this.status === ParseStatus.ON_PARSE) {
      throw new Error(`unexpected token '${this.jsonStr[this._index]}' at index: ${this._index} . ${errMsg} `);
    }else{
      let errToken = this._tokenList[this._generateIndex]
      throw new Error(`unexpected token '${errToken.value}' at index: ${errToken.index}`)
    }
  }

  getToken(char) {
    let tokenObj;
    switch (char) {
      case "{":
        tokenObj = {
          type: TokenType.LEFT_OBJECT,
          value: char,
        };
        break;
      case "}":
        tokenObj = {
          type: TokenType.RIGHT_OBJECT,
          value: char,
        };
        break;
      case "[":
        tokenObj = {
          type: TokenType.LEFT_ARRAY,
          value: char,
        };
        break;
      case "]":
        tokenObj = {
          type: TokenType.RIGHT_ARRAY,
          value: char,
        };
        break;
      case ",":
        tokenObj = {
          type: TokenType.COMMA,
          value: char,
        };
        break;
      case ":":
        tokenObj = {
          type: TokenType.COLON,
          value: char,
        };
        break;
      case '"':
        tokenObj = {
          type: TokenType.STRING,
          value: this.getString(),
        };
        break;
    }

    // 已有匹配token则不进行其他token类型的判断
    if (tokenObj) {
      this._index++;
      return tokenObj;
    }

    if (this.isNumber(char)) {
      tokenObj = {
        type: TokenType.NUMBER,
        value: this.getNumber(),
      };
    }

    if (char === "t" || char === "f" || char === "n") {
      let value = this.getBooleanOrNull();
      tokenObj = {
        type: value === null ? TokenType.NULL : TokenType.BOOLEAN,
        value: value,
      };
    }

    if (!tokenObj) {
      this.emitError("transform error");
    } else {
      return tokenObj;
    }
  }

  // 判断是否是基础数据类型
  isBasicType(token) {
    return [TokenType.BOOLEAN, TokenType.STRING, TokenType.NUMBER, TokenType.NULL].includes(token.type);
  }
  isNumber(num) {
    return num >= "0" && num <= "9";
  }

  getString() {
    let str = '"';
    let startIndex = this._index;
    this._index++;
    // 找到闭合字符串标记
    while (this.jsonStr[this._index] !== '"' && this._index < this.jsonStr.length) this._index++;
    if (this._index === this.jsonStr.length) return this.emitError("非法字符串");
    // 去除两边的引号
    str = this.jsonStr.slice(startIndex + 1, this._index);
    return str;
  }
  getNumber() {
    let numStr = "";
    let startIndex = this._index;
    let hasDot = false;
    while ((this.isNumber(this.jsonStr[this._index]) || this.jsonStr[this._index] === ".") && this._index < this.jsonStr.length) {
      // 非法浮点数判断
      if (this.jsonStr[this._index] === ".") {
        // 小数点出现多次则非法
        if (hasDot) {
          return this.emitError("unexpected .");
        }
        // 小数点出现在第一位或最后一位则非法
        if (this._index === 0 || this._index === this.jsonStr.length - 1) {
          return this.emitError("unexpected .");
        }
        // 小数点前或后一位数字不是数字则非法
        if (!this.isNumber(this.jsonStr[this._index - 1]) || !this.isNumber(this.jsonStr[this._index + 1])) {
          return this.emitError("unexpected .");
        }
        hasDot = true;
      }
      this._index++;
    }
    numStr = this.jsonStr.slice(startIndex, this._index);
    let num = Number(numStr);
    if (isNaN(num)) return this.emitError("invalid number");
    return num;
  }

  getBooleanOrNull() {
    if (this.jsonStr.substr(this._index, 4) === "true") {
      this._index += 4;
      return true;
    }
    if (this.jsonStr.substr(this._index, 5) === "false") {
      this._index += 5;
      return false;
    }

    if (this.jsonStr.substr(this._index, 4) === "null") {
      this._index += 4;
      return null;
    }

    this.emitError("未知的值");
  }
}

function parseJson (jsonStr, receiver) {
  const jParser = new JParser(jsonStr, receiver);
  return jParser.generate()
}
module.exports = parseJson;
